﻿//"C:\Program Files\Microsoft SDKs\Windows\v6.0A\Bin\sqlmetal.exe" Data.sdf /dbml:Data.dbml
//ExportSQLCE "Data Source=Data.sdf;" Data.sql
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.IO;
using System.Collections.Specialized;
using System.Web;
using Argotic.Common;
using PeapodCatcher.Properties;
using System.Text.RegularExpressions;
using Argotic.Syndication;

namespace PeapodCatcher {
    public class Updater {
        private string _baseDownloadFolder;

        public Updater() {
            _baseDownloadFolder = Environment.ExpandEnvironmentVariables(Settings.Default.DownloadFolder);
            CheckDirectory(_baseDownloadFolder);

        }

        public void Refresh() {

            try {
                List<Feed> feeds;
                using (Data db = new Data(Common.DatabasePath)) {
                    feeds = (from f in db.Feed select f).ToList();
                }

                for (int f = 0; f < feeds.Count; f++) {
                    Feed feed = feeds[f];

                    WebClient client = new WebClient();

                    //IdleTime idle = new IdleTime();
                    RssFeed rss = null;
                    try
                    {
                        rss = RssFeed.Create(new Uri(feed.Url));
                    }
                    catch(Exception ex)
                    {
                        Log.LogError("RssFeed Exception - " + feed.Url, ex);
                        continue;
                    }

                    CleanupFiles(rss, feed);
                    CleanupDirectories(feed);
                    int max = rss.Channel.Items.Count();
                    if (feed.KeepAtMost > 0) {
                        max = feed.KeepAtMost.GetValueOrDefault();
                    }

                    for (int i = 0; i < max; i++) {
                        if (rss.Channel.Items.Count() <= i) break;

                        RssItem rssItem = rss.Channel.Items.ElementAt<RssItem>(i);
                        try {
                            // Stop downloading if user becomes active
                            // Note: Does not currently work in Vista
                            //if (idle.GetLastUserInput() < (Properties.Settings.Default.IdleTime * 60))
                            //{
                            //    break;
                            //}
                            foreach (RssEnclosure enclosure in rssItem.Enclosures) {
                                string uri = enclosure.Url.AbsoluteUri;
                                string subPath = feed.UseSubPath ? (feed.SubPathName ?? string.Empty) : string.Empty;
                                string tempFilePath = FormatTempPath(Environment.GetFolderPath(Environment.SpecialFolder.InternetCache), Path.GetFileNameWithoutExtension(uri), Path.GetExtension(uri));
                                string destDir = Path.Combine(_baseDownloadFolder, subPath);
                                if (feed.ParseMediaFlyShows.GetValueOrDefault()) {
                                    string show = GetMediaFlyShowName(rssItem);
                                    destDir = Path.Combine(destDir, show);
                                }
                                string destFilePath = FormatDestinationPath(destDir, Path.GetFileNameWithoutExtension(uri), Path.GetExtension(uri));

                                FileInfo destFile = new FileInfo(destFilePath);

                                if (destFile.Exists) continue;

                                Log.LogMessage("Updater", "Downloading File: " + Path.GetFileName(uri));

                                ////Make fake file for testing
                                //using (TextWriter tw = new StreamWriter(tempFilePath, true))
                                //{
                                //    tw.WriteLine("Demo");
                                //    tw.Close();
                                //}
                                client.DownloadFile(uri, tempFilePath);

                                FileInfo tempFile = new FileInfo(tempFilePath);

                                // Everything went well, lets finish up
                                if (tempFile.Exists) {
                                    MoveToDestination(rssItem, destDir, destFilePath, tempFile, feed);

                                    if (feed.Push) {
                                        string espisodeTitle = feed.Title;
                                        if (feed.ParseMediaFlyShows.GetValueOrDefault()) {
                                            espisodeTitle = GetMediaFlyShowName(rssItem);
                                        }
                                        string description = Regex.Replace(rssItem.Description ?? string.Empty, @"<(.|\n)*?>", string.Empty);
                                        CreateTivoMetadata(espisodeTitle, rssItem.Title, description, destFilePath, rssItem.PublicationDate.Year);
                                        //PushToTivo(destDir, destFilePath);
                                    }

                                    Log.LogMessage("Updater", "Download Complete");
                                }
                            }
                        }
                        catch (Exception ex) {
                            Log.LogError("General Feed Exception", ex);
                        }
                    }
                }
            }
            catch (Exception ex) {
                Log.LogError("General Updater Exception", ex);
            }

        }

        private string FormatTempPath(string tempDirectory, string title, string extension) {
            string cleanedExtension = extension;
            // clean extension
            if (extension.Contains('?')) {
                cleanedExtension = extension.Substring(0, extension.IndexOf('?'));
            }
            int baseLen = tempDirectory.Length + cleanedExtension.Length;
            int totalLen = baseLen + title.Length;
            if (totalLen > 255) {
                title = title.Substring(0, title.Length - (totalLen - 255) - 1);
            }
            return Path.Combine(tempDirectory, title + cleanedExtension);
        }

        private void CreateTivoMetadata(string seriesTitle, string episodeTitle, string description, string destinationFile, int year) {
            string metaData = @"title : {0}
movieYear : {3}
description : ""{1}"" {2}
isEpisode : false
starRating : 
mpaaRating : 
vProgramGenre : 
vDirector : 
vActor : 
";
            metaData = String.Format(metaData, seriesTitle, episodeTitle, description, year);
            string metaFile = destinationFile + ".txt";
            using (TextWriter tw = new StreamWriter(metaFile, true)) {
                tw.Write(metaData);
                tw.Close();
            }
        }

        private void MoveToDestination(RssItem rssItem, string destDir, string destFilePath, FileInfo tempFile, Feed feed) {
            CheckDirectory(destDir, true);
            tempFile.MoveTo(destFilePath);
            tempFile.CreationTime = rssItem.PublicationDate;
            tempFile.LastWriteTime = rssItem.PublicationDate;
            AddFile(destFilePath, Path.GetFileName(GetFileUri(rssItem)), rssItem.PublicationDate, feed);
            UpdateDirectory(destDir);
        }

        private static void PushToTivo(string destDir, string destFilePath) {
            // TODO: Generate metadata file.
            // TODO: Figure out why certain HD files transfer choppy.

            DirectoryInfo di = new DirectoryInfo(destDir);
            string file = HttpUtility.UrlEncode(
                Path.Combine("\\" + di.Name,
                                    Path.GetFileName(destFilePath)));
            string container = HttpUtility.UrlEncode(Settings.Default.PyTivoShare);
            string tivo = HttpUtility.UrlEncode(Settings.Default.TivoName);
            string pushUrl = String.Format(
                "http://localhost:9032/TiVoConnect?Command=Push&Container={0}&File={1}&tsn={2}",
                container, file, tivo);
            try {
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(pushUrl);
                HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                Log.LogMessage("Updater", "Tivo Push Response: " + response.StatusCode);
            }
            catch (Exception ex) {
                Log.LogError("Push To Tivo Exception: " + pushUrl, ex);
            }
        }

        private void UpdateDirectory(string destDir) {
            DirectoryInfo dir = new DirectoryInfo(destDir);
            FileInfo[] files = dir.GetFiles();
            if (files.Length > 0) {
                try {
                    // Reset Date for PyTivo (PyTivo uses directory last write time for the display date, 
                    // instead of most the most recent file, so we reset the directory last write date)
                    dir.LastWriteTime = GetMostRecentFileTime(files);
                }
                catch (Exception ex) {
                    Log.LogError("Failed to update directory LastWriteTime", ex);
                }
            }
        }

        private StringCollection GetDirectories(Feed feed) {
            StringCollection collection = new StringCollection();
            //try
            //{
            //    using (Data db = new Data(Common.DatabasePath))
            //    {
            //        var directories = from d in db.Directory select d;
            //        foreach (Directory directory in directories)
            //        {
            //            collection.Add(directory.Path as string);
            //        }
            //    }
            //}
            //catch (Exception ex)
            //{
            //    Log.LogError("Database Exception (Get Directories)", ex);
            //}
            return collection;
        }

        private StringCollection GetFiles(RssFeed rss, Feed feed) {
            StringCollection collection = new StringCollection();
            try {
                using (Data db = new Data(Common.DatabasePath)) {
                    var files = from f in db.File where f.FeedID == feed.ID select f;
                    foreach (File file in files) {
                        collection.Add(file.Path as string);
                    }
                }
            }
            catch (Exception ex) {
                Log.LogError("Database Exception (Get Files)", ex);
            }
            return collection;
        }

        private void AddFile(string filePath, string originalFileName, DateTime postDate, Feed feed) {
            try {

                using (Data db = new Data(Common.DatabasePath)) {
                    File file = new File() {
                        ID = Guid.NewGuid(),
                        FeedID = feed.ID,
                        Path = filePath,
                        OriginalFileName = originalFileName,
                        DownloadDate = DateTime.Now,
                        PostDate = postDate
                    };
                    db.File.InsertOnSubmit(file);
                    db.SubmitChanges();
                }
            }
            catch (Exception ex) {
                Log.LogError("Database Exception (Add File)", ex);
            }
        }

        private void AddMediaFlyShow(Feed feed, string showName, string subPathName) {
            try {
                using (Data db = new Data(Common.DatabasePath)) {

                    MediaFlyShow show = new MediaFlyShow() {
                        ID = Guid.NewGuid(),
                        FeedID = feed.ID,
                        ShowName = showName,
                        UseSubPath = true,
                        SubPathName = subPathName,
                    };
                    db.MediaFlyShow.InsertOnSubmit(show);
                    db.SubmitChanges();
                }
            }
            catch (Exception ex) {
                Log.LogError("Database Exception (Add Directory)", ex);
            }
        }

        private string FormatDestinationPath(string destDirectory, string title, string extension) {
            string cleanedExtension = extension;
            // clean extension
            if (extension.Contains('?')) {
                cleanedExtension = extension.Substring(0, extension.IndexOf('?'));
            }
            int baseLen = destDirectory.Length + cleanedExtension.Length;
            int totalLen = baseLen + title.Length;
            if (totalLen > 255) {
                title = title.Substring(0, title.Length - (totalLen - 255) - 1);
            }
            return Path.Combine(destDirectory, title + cleanedExtension);
        }


        private void CheckDirectory(string path) {
            DirectoryInfo di = new DirectoryInfo(path);
            if (!di.Exists) {
                di.Create();
            }
        }

        private void CheckDirectory(string path, bool addToDatabase) {
            DirectoryInfo di = new DirectoryInfo(path);
            if (!di.Exists) {
                di.Create();
            }
        }

        private void CleanupFiles(RssFeed rss, Feed feed) {
            StringCollection fileManifiest = GetFiles(rss, feed);

            DirectoryInfo dir = new DirectoryInfo(_baseDownloadFolder);
            FileInfo[] files = dir.GetFiles("*.*", SearchOption.AllDirectories);
            foreach (FileInfo file in files) {
                if (!fileManifiest.Contains(file.FullName) || file.CreationTime >= DateTime.Now.AddDays(-1)) continue;
                if (!IsCurrentFile(file.Name, rss.Channel.Items, feed)) {
                    try {
                        Log.LogMessage("Updater", "Removing File: " + file.Name);
                        RemoveFile(file.FullName);
                        file.Delete();
                    }
                    catch (Exception ex) {
                        Log.LogError("Cleanup File Exception", ex);
                    }
                }
            }
        }

        private void CleanupDirectories(Feed feed) {
            StringCollection directoryManifiest = GetDirectories(feed);

            DirectoryInfo root = new DirectoryInfo(_baseDownloadFolder);
            DirectoryInfo[] directories = root.GetDirectories("*", SearchOption.AllDirectories);
            foreach (DirectoryInfo dir in directories) {
                if (!directoryManifiest.Contains(dir.FullName)) continue;
                FileInfo[] files = dir.GetFiles();

                if (files.Length == 0) {
                    Log.LogMessage("Updater", "Removing Directory: " + dir.Name);
                    RemoveDirectory(dir.FullName);

                }
                else {
                    // Reset Date for PyTivo (PyTivo uses directory last write time for the display date, 
                    // instead of most the most recent file, so we reset the directory last write date)
                    UpdateDirectory(dir.FullName);
                }
            }
        }

        private void RemoveFile(string filePath) {
            try {
                using (Data db = new Data(Common.DatabasePath)) {
                    File file = (from f in db.File where f.Path == filePath select f).SingleOrDefault<File>();
                    file.Deleted = true;
                    db.SubmitChanges();
                }
            }
            catch (Exception ex) {
                Log.LogError("Remove File Exception", ex);
            }
        }

        private void RemoveDirectory(string directoryPath) {
            //try
            //{
            //    DirectoryInfo di = new DirectoryInfo(directoryPath);
            //    if (di.Exists)
            //    {
            //        di.Delete();
            //    }

            //    using (Data db = new Data(Common.DatabasePath))
            //    {
            //        Directory directory = (from d in db.Directory where d.Path == directoryPath select d).SingleOrDefault<Directory>();
            //        db.Directory.DeleteOnSubmit(directory);
            //        db.SubmitChanges();
            //    }
            //}
            //catch (Exception ex)
            //{
            //    Log.LogError("Remove Directory Exception", ex);
            //}
        }

        private DateTime GetMostRecentFileTime(FileInfo[] files) {
            DateTime maxDate = new DateTime();
            foreach (FileInfo file in files) {
                if (file.CreationTime > maxDate) {
                    maxDate = file.CreationTime;
                }
            }
            return maxDate;
        }

        private bool IsCurrentFile(string p, IEnumerable<RssItem> items, Feed feed) {
            return true;

            foreach (RssItem item in items) {
                string show = GetMediaFlyShowName(item);
                string uri = GetFileUri(item);
                string destDir = Path.Combine(_baseDownloadFolder, show);
                string destFilePath = FormatDestinationPath(destDir, Common.SafeFileName(item.Title), Path.GetExtension(uri));

                if (Path.GetFileName(destFilePath) == p) {
                    return true;
                }
            }
            return false;
        }

        private string GetMediaFlyShowName(RssItem item) {
            string magic = "http://www.mediafly.com/Podcasts/Feeds/";
            string show = string.Empty;
            show = item.Link.AbsoluteUri;
            int len = show.IndexOf("#") - magic.Length;
            show = show.Substring(magic.Length, len).Replace('_', ' ');
            if (show.EndsWith(" 1")) {
                show = show.Substring(0, show.Length - 2);
            }
            return show;
        }

        private string GetFileUri(RssItem item) {
            string uri = string.Empty;
            foreach (RssEnclosure link in item.Enclosures) {
                uri = link.Url.AbsoluteUri;
                break;
            }
            return uri;

        }
    }

}


